#ifndef CPU_INSTR_H
#define CPU_INSTR_H

#include "types.h"

static inline uint8_t inb(uint16_t port) {
    uint8_t rv;
    // inb dx, al 
    __asm__ __volatile__("inb %1, %0" :"=a"(rv) :"d"(port));
    return rv;
}

static inline uint16_t inw(uint16_t port) {
    uint16_t rv;
    // inw dx, ax 
    __asm__ __volatile__("in %1, %0" :"=a"(rv) :"d"(port));
    return rv;
}

static inline void outb(uint16_t port, uint8_t data) {
    // outb al, dx
    __asm__ __volatile__("outb %0, %1" ::"a"(data), "d"(port));
}

static inline void outw(uint16_t port, uint16_t data) {
    // outw ax, dx
    __asm__ __volatile__("outw %0, %1" ::"a"(data), "d"(port));
}

static inline void cli(void) {
    __asm__ __volatile__("cli");
}

static inline void sti(void) {
    __asm__ __volatile__("sti");
}

static inline void lgdt(uint32_t start, uint32_t size) {
    struct {
        uint16_t limit;
        uint16_t start15_0;
        uint16_t start31_16;
    } gdt;

    gdt.start31_16 = start >> 16;
    gdt.start15_0 = start & 0xFFFF;
    gdt.limit = size - 1;

    __asm__ __volatile__("lgdt %0" ::"m"(gdt));
}

static inline void lidt(uint32_t start, uint32_t size) {
    struct {
        uint16_t limit;
        uint16_t start15_0;
        uint16_t start31_16;
    } idt;

    idt.start31_16 = start >> 16;
    idt.start15_0 = start & 0xFFFF;
    idt.limit = size - 1;

    __asm__ __volatile__("lidt %0" ::"m"(idt));
}

static inline uint32_t read_cr0(void) {
    uint32_t cr0;
    __asm__ __volatile__("mov %%cr0, %0" :"=r"(cr0));
    return cr0;
}

static inline void write_cr0(uint32_t v) {
    __asm__ __volatile__("mov %0, %%cr0" ::"r"(v));
}

static inline uint32_t read_cr2(void) {
    uint32_t cr2;
    __asm__ __volatile__("mov %%cr2, %0" :"=r"(cr2));
    return cr2;
}

static inline uint32_t read_cr3(void) {
    uint32_t cr3;
    __asm__ __volatile__("mov %%cr3, %0" :"=r"(cr3));
    return cr3;
}

static inline void write_cr3(uint32_t v) {
    __asm__ __volatile__("mov %0, %%cr3" ::"r"(v));
}

static inline uint32_t read_cr4(void) {
    uint32_t cr4;
    __asm__ __volatile__("mov %%cr4, %0" :"=r"(cr4));
    return cr4;
}

static inline void write_cr4(uint32_t v) {
    __asm__ __volatile__("mov %0, %%cr4" ::"r"(v));
}


static inline void far_jump(uint32_t selector, uint32_t offset) {
    uint32_t addr[] = { offset, selector };
    __asm__ __volatile__("ljmpl *(%0)" ::"r"(addr));
}

static inline void hlt(void) {
    __asm__ __volatile__("hlt");
}

static inline void write_tr(uint16_t tss_sel) {
    __asm__ __volatile__("ltr %%ax" ::"ax"(tss_sel));
}

static inline uint32_t read_eflags(void) {
    uint32_t eflags;
    __asm__ __volatile__("pushf\n\tpop %%eax" :"=a"(eflags));
    return eflags;
} 

static inline void write_eflags(uint32_t eflags) {
    __asm__ __volatile__("push %%eax\n\tpopf" ::"a"(eflags));
}


#endif